﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Data.Common;
using System.Collections;

namespace Kailua.net.windward.utils.ado.SPList.custom
{
    /// <summary>
    /// A query parameter.
    /// </summary>
    public class SPListParameter : DbParameter, IDataParameter
    {
        #region Fields

        DbType dbType  = DbType.Object;
        ParameterDirection direction = ParameterDirection.Input;
        bool isNullable = false;
        string parameterName;
        int size;
        string sourceColumn;
        bool sourceColumnNullMapping;
        DataRowVersion sourceVersion = DataRowVersion.Current;
        object value;

        #endregion Fields

        #region Constructors

        /// <summary>
        /// Creates a new SPListParameter.
        /// </summary>
        public SPListParameter()
            : base()
        {
        }

        #endregion Constructors

        #region Properties

        /// <summary>
        /// Gets or sets the DbType.
        /// </summary>
        public override DbType DbType
        {
            get { return dbType; }
            set { dbType = value; }
        }
        /// <summary>
        /// Gets or sets the ParameterDirection.
        /// </summary>
        public override ParameterDirection Direction
        {
            get { return direction; }
            set { direction = value; }
        }
        /// <summary>
        /// Gets or sets whether this parameter is nullable.
        /// </summary>
        public override Boolean IsNullable
        {
            get { return isNullable; }
            set { isNullable = value; }
        }
        /// <summary>
        /// Gets or sets the parameter's name.
        /// </summary>
        public override String ParameterName
        {
            get { return parameterName; }
            set { parameterName = value; }
        }
        /// <summary>
        /// Gets or sets the maximum size in bytes of the parameter's value.
        /// </summary>
        public override int Size
        {
            get { return size; }
            set { size = value; }
        }
        /// <summary>
        /// Gets or sets the source column name.
        /// </summary>
        public override String SourceColumn
        {
            get { return sourceColumn; }
            set { sourceColumn = value; }
        }
        /// <summary>
        /// Gets or sets whether the source column is nullable.
        /// </summary>
        public override bool SourceColumnNullMapping
        {
            get { return sourceColumnNullMapping; }
            set { sourceColumnNullMapping = value; }
        }
        /// <summary>
        /// Gets or sets the System.Data.DataRowVersion to use when you load System.Data.Common.DbParameter.Value.
        /// </summary>
        public override DataRowVersion SourceVersion
        {
            get { return sourceVersion; }
            set { sourceVersion = value; }
        }
        /// <summary>
        /// Gets or sets the parameter's value.
        /// </summary>
        public override object Value
        {
            get { return value; }
            set
            {
                this.value = value;
                dbType = InferType(value);
            }
        }

        #endregion Properties

        #region Methods

        /// <summary>
        /// Returns the DbType of the specified <paramref name="value"/>.
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        private static DbType InferType(Object value)
        {
            switch (Type.GetTypeCode(value.GetType()))
            {
                case TypeCode.Empty:
                    throw new SystemException("Invalid data type, Empty.");

                case TypeCode.Object:
                    return DbType.Object;
                case TypeCode.Char:
                    return DbType.String;
                case TypeCode.SByte:
                    return DbType.SByte;
                case TypeCode.UInt16:
                    return DbType.UInt16;
                case TypeCode.UInt32:
                    return DbType.UInt32;
                case TypeCode.UInt64:
                    return DbType.UInt64;
                case TypeCode.Boolean:
                    return DbType.Boolean;
                case TypeCode.Byte:
                    return DbType.Byte;
                case TypeCode.Int16:
                    return DbType.Int16;
                case TypeCode.Int32:
                    return DbType.Int32;
                case TypeCode.Int64:
                    return DbType.Int64;
                case TypeCode.Single:
                    return DbType.Single;
                case TypeCode.Double:
                    return DbType.Double;
                case TypeCode.Decimal:
                    return DbType.Decimal;
                case TypeCode.DateTime:
                    return DbType.DateTime;
                case TypeCode.String:
                    return DbType.String;
                default:
                    throw new SystemException("Value is of unknown data type");
            }
        }
        /// <summary>
        /// Resets the DbType
        /// </summary>
        public override void ResetDbType()
        {
            dbType = DbType.Object;
        }

        #endregion Methods
    }

    /// <summary>
    /// A collection of SPListParameter objects.
    /// </summary>
    public class SPListParameterCollection : DbParameterCollection, IDataParameterCollection
    {
        #region Fields

        private ArrayList parameters;

        #endregion Fields

        #region Constructors

        /// <summary>
        /// Creates a new SPListParameterCollection.
        /// </summary>
        public SPListParameterCollection()
            : base()
        {
            parameters = new ArrayList();
        }

        #endregion Constructors

        #region Properties

        /// <summary>
        /// Gets the number of items in the collection.
        /// </summary>
        public override int Count
        {
            get { return parameters.Count; }
        }
        /// <summary>
        /// Gets whether the collection is fixed size.
        /// </summary>
        public override bool IsFixedSize
        {
            get { return parameters.IsFixedSize; }
        }
        /// <summary>
        /// Gets whether the collection is readonly.
        /// </summary>
        public override bool IsReadOnly
        {
            get { return parameters.IsReadOnly; }
        }
        /// <summary>
        /// Gets whether the collection is synchronized.
        /// </summary>
        public override bool IsSynchronized
        {
            get { return parameters.IsSynchronized; }
        }
        /// <summary>
        /// Gets an object that can be used to synchronize access to the collection.
        /// </summary>
        public override object SyncRoot
        {
            get { return parameters.SyncRoot; }
        }

        #endregion Properties

        #region Methods

        /// <summary>
        /// Adds the parameter to the collection.
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public override int Add(object value)
        {
            if (!(value is SPListParameter))
                throw new DataException(string.Format("Cannot add object of type {0} to SPListParameterCollection.", value.GetType().ToString()));

            parameters.Add(value);

            return parameters.Count - 1;
        }
        /// <summary>
        /// Adds the array of values to the parameter collection.
        /// </summary>
        /// <param name="values"></param>
        public override void AddRange(Array values)
        {
            ValidateRange(values);

            foreach (SPListParameter param in values)
                parameters.Add(param);
        }
        /// <summary>
        /// Clears the collection.
        /// </summary>
        public override void Clear()
        {
            parameters.Clear();
        }
        /// <summary>
        /// Returns true if the collection contains a parameter with the specified name.
        /// </summary>
        /// <param name="value">A non-null string value.</param>
        /// <returns></returns>
        public override bool Contains(string value)
        {
            if (value == null)
                throw new ArgumentNullException();

            foreach (SPListParameter param in parameters)
                if (value.Equals(param.ParameterName))
                    return true;

            return false;
        }
        /// <summary>
        /// Returns true if the collection contains a parameter with the specified value.
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public override bool Contains(object value)
        {
            foreach (SPListParameter param in parameters)
            {
                if (value == null)
                {
                    if (param.Value == null)
                        return true;
                }
                else
                {
                    if (value.Equals(param.Value))
                        return true;
                }
            }

            return false;
        }
        /// <summary>
        /// Copies an array of items to the collection starting at the specified index.
        /// </summary>
        /// <param name="array"></param>
        /// <param name="index"></param>
        public override void CopyTo(Array array, int index)
        {
            ValidateRange(array);

            parameters.CopyTo(array, index);
        }
        /// <summary>
        /// Throws a DataException if any value is not of type SPListParameter.
        /// </summary>
        /// <param name="values"></param>
        private static void ValidateRange(Array values)
        {
            foreach (object val in values)
                if (!(val is SPListParameter))
                    throw new DataException(string.Format("Cannot add object of type {0} to SPListParameterCollection.", val.GetType().ToString()));
        }
        /// <summary>
        /// Gets an enumerator for the collection.
        /// </summary>
        /// <returns></returns>
        public override System.Collections.IEnumerator GetEnumerator()
        {
            return parameters.GetEnumerator();
        }
        /// <summary>
        /// Gets the specified SPListParameter or null if no such parameter exists.
        /// </summary>
        /// <param name="parameterName"></param>
        /// <returns></returns>
        protected override DbParameter GetParameter(string parameterName)
        {
            if (parameterName == null)
                throw new ArgumentNullException();

            foreach (SPListParameter param in parameters)
                if (parameterName.Equals(param.ParameterName))
                    return param;

            throw new ArgumentOutOfRangeException(string.Format("No SPListParameter named {0}.", parameterName));
        }
        /// <summary>
        /// Gets the specified SPListParameter.
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        protected override DbParameter GetParameter(int index)
        {
            return (SPListParameter)parameters[index];
        }
        /// <summary>
        /// Returns the index of the parameter with the specified name.
        /// </summary>
        /// <param name="parameterName"></param>
        /// <returns></returns>
        public override int IndexOf(string parameterName)
        {
            if (parameterName == null)
                throw new ArgumentNullException();

            int index = 0;
            foreach (SPListParameter param in parameters)
                if (parameterName.Equals(param.ParameterName))
                    return index;
                else
                    index++;

            throw new ArgumentOutOfRangeException(string.Format("No SPListParameter named {0}.", parameterName));
        }
        /// <summary>
        /// Returns the index of the parameter with the specified value.
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public override int IndexOf(object value)
        {
            int index = 0;
            foreach (SPListParameter param in parameters)
                if (value.Equals(param.Value))
                    return index;
                else
                    index++;

            throw new ArgumentOutOfRangeException(string.Format("No SPListParameter having value {0}.", value.ToString()));
        }
        /// <summary>
        /// Inserts the parameter at the specified index in the collection.
        /// </summary>
        /// <param name="index"></param>
        /// <param name="value"></param>
        public override void Insert(int index, object value)
        {
            if (!(value is SPListParameter))
                throw new DataException(string.Format("Cannot add object of type {0} to SPListParameterCollection.", value.GetType().ToString()));

            parameters.Insert(index, value);
        }
        /// <summary>
        /// Removes the specified parameter from the list.
        /// </summary>
        /// <param name="value"></param>
        public override void Remove(object value)
        {
            parameters.Remove(value);
        }
        /// <summary>
        /// Removes the parameter having the specified name from the collection.
        /// </summary>
        /// <param name="parameterName"></param>
        public override void RemoveAt(string parameterName)
        {
            if (parameterName == null)
                throw new ArgumentNullException();

            foreach (SPListParameter param in parameters)
                if (parameterName.Equals(param.ParameterName))
                    parameters.Remove(param);

            throw new ArgumentOutOfRangeException(string.Format("No SPListParameter named {0}.", parameterName));
        }
        /// <summary>
        /// Removes the parameter at the specified index from the collection.
        /// </summary>
        /// <param name="index"></param>
        public override void RemoveAt(int index)
        {
            parameters.RemoveAt(index);
        }
        /// <summary>
        /// Replaces the parameter having the specified name with the specified parameter.
        /// </summary>
        /// <param name="parameterName"></param>
        /// <param name="value"></param>
        protected override void SetParameter(string parameterName, DbParameter value)
        {
            if (parameterName == null)
                throw new ArgumentNullException();

            int index = 0;
            foreach (SPListParameter param in parameters)
            {
                if (parameterName.Equals(param.ParameterName))
                {
                    parameters[index] = value;
                    return;
                }
                index++;
            }

            throw new ArgumentOutOfRangeException(string.Format("No SPListParameter named {0}.", parameterName));
        }
        /// <summary>
        /// Replaces the parameter at the specified index with the specified parameter.
        /// </summary>
        /// <param name="index"></param>
        /// <param name="value"></param>
        protected override void SetParameter(int index, DbParameter value)
        {
            parameters[index] = value;
        }

        #endregion Methods
    }
}
